package com.ruoyi.common.utils.mapIp;

import java.util.Arrays;

public class MapPointUtils {

    public static boolean isPointInPolygon(MapPoint point, MapPoint[] boundaryPoints) {
        if (boundaryPoints != null && boundaryPoints.length > 0 && boundaryPoints[boundaryPoints.length - 1].equals(boundaryPoints[0])) {
            boundaryPoints = Arrays.copyOf(boundaryPoints, boundaryPoints.length - 1);
        }
        int pointCount = boundaryPoints.length;
        if (!isPointInRectangle(point, boundaryPoints)) {
            return false;
        }
        for (int i = 0; i < pointCount; i++) {
            if (point.equals(boundaryPoints[i])) {
                return true;
            }
        }
        int intersectPointCount = 0;
        float intersectPointWeights = 0;
        double precision = 2e-10;
        MapPoint point1 = boundaryPoints[0], point2;
        for (int i = 1; i <= pointCount; i++) {
            point2 = boundaryPoints[i % pointCount];
            if (point.getLat() < Math.min(point1.getLat(), point2.getLat()) || point.getLat() > Math.max(point1.getLat(), point2.getLat())) {
                point1 = point2;
                continue;
            }
            if (point.getLat() > Math.min(point1.getLat(), point2.getLat()) && point.getLat() < Math.max(point1.getLat(), point2.getLat())) {
                if (point1.getLng() == point2.getLng()) {
                    if (point.getLng() == point1.getLng()) {
                        return true;
                    } else if (point.getLng() < point1.getLng()) {
                        ++intersectPointCount;
                    }
                } else {
                    if (point.getLng() <= Math.min(point1.getLng(), point2.getLng())) {
                        ++intersectPointCount;
                    } else if (point.getLng() > Math.min(point1.getLng(), point2.getLng()) && point.getLng() < Math.max(point1.getLng(), point2.getLng())) {
                        double slopeDiff = getSlopeDiff(point, point1, point2);
                        if (slopeDiff > 0) {
                            if (slopeDiff < precision) {
                                return true;
                            } else {
                                intersectPointCount++;
                            }
                        }
                    }
                }
            } else {
                if (point1.getLat() == point2.getLat()) {
                    if (checkPointInLine(point, point1, point2)) {
                        return true;
                    }
                }
                if (((point.getLat() == point1.getLat() && point.getLng() < point1.getLng())) || (point.getLat() == point2.getLat() && point.getLng() < point2.getLng())) {
                    if (point2.getLat() < point1.getLat()) {
                        intersectPointWeights += -0.5;
                    } else if (point2.getLat() > point1.getLat()) {
                        intersectPointWeights += 0.5;
                    }
                }
            }
            point1 = point2;
        }
        return (intersectPointCount + Math.abs(intersectPointWeights)) % 2 != 0;
    }

    private static double getSlopeDiff(MapPoint point, MapPoint point1, MapPoint point2) {
        double slopeDiff = 0.0d;
        if (point1.getLat() > point2.getLat()) {
            slopeDiff = (point.getLat() - point2.getLat()) / (point.getLng() - point2.getLng()) - (point1.getLat() - point2.getLat()) / (point1.getLng() - point2.getLng());
        } else {
            slopeDiff = (point.getLat() - point1.getLat()) / (point.getLng() - point1.getLng()) - (point2.getLat() - point1.getLat()) / (point2.getLng() - point1.getLng());
        }
        return slopeDiff;
    }

    private static boolean checkPointInLine(MapPoint point, MapPoint point1, MapPoint point2) {
        return point.getLng() <= Math.max(point1.getLng(), point2.getLng()) && point.getLng() >= Math.min(point1.getLng(), point2.getLng());
    }

    public static boolean isPointInRectangle(MapPoint point, MapPoint[] boundaryPoints) {
        MapPoint southWestPoint = getSouthWestPoint(boundaryPoints);
        MapPoint northEastPoint = getNorthEastPoint(boundaryPoints);
        return (point.getLng() >= southWestPoint.getLng() && point.getLng() <= northEastPoint.getLng() && point.getLat() >= southWestPoint.getLat() && point.getLat() <= northEastPoint.getLat());
    }

    private static MapPoint getSouthWestPoint(MapPoint[] vertexs) {
        double minLng = vertexs[0].getLng(), minLat = vertexs[0].getLat();
        for (MapPoint bmapPoint : vertexs) {
            double lng = bmapPoint.getLng();
            double lat = bmapPoint.getLat();
            if (lng < minLng) {
                minLng = lng;
            }
            if (lat < minLat) {
                minLat = lat;
            }
        }
        return new MapPoint(minLng, minLat);
    }

    private static MapPoint getNorthEastPoint(MapPoint[] vertexs) {
        double maxLng = 0.0d, maxLat = 0.0d;
        for (MapPoint bmapPoint : vertexs) {
            double lng = bmapPoint.getLng();
            double lat = bmapPoint.getLat();
            if (lng > maxLng) {
                maxLng = lng;
            }
            if (lat > maxLat) {
                maxLat = lat;
            }
        }
        return new MapPoint(maxLng, maxLat);
    }

}
